package com.uxwine.alarm.mon;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;

import com.uxwine.alarm.Alarm;
import com.uxwine.alarm.AlarmData;
import com.uxwine.alarm.Settings;
import com.uxwine.alarm.Settings.Snooze;
import com.uxwine.util.Log;

public class AlarmUtil extends AlarmData
{
	/** 
	 * This action triggers the AlarmReceiver as well as the AlarmService. It 
	 * is a public action used in the manifest for receiving Alarm broadcast 
	 * from the alarm manager.
	 * */
    public static final String ACTION_ALARM_ALERT = "com.uxwine.alarm.ALARM_ALERT";

    /**
     * A public action sent by AlarmService when the alarm has stopped sounding 
     * for any reason (e.g. because it has been dismissed from AlarmAlertFullScreen,
     * or killed due to an incoming phone call, etc).
     */
    public static final String ACTION_ALARM_DONE = "com.uxwine.alarm.ALARM_DONE";

    /** 
     * AlarmAlertFullScreen listens for this broadcast intent, so that other applications 
     * can snooze the alarm (after ACTION_ALARM_ALERT and before ACTION_ALARM_DONE).
     */
    public static final String ACTION_ALARM_SNOOZE = "com.uxwine.alarm.ALARM_SNOOZE";

    /**
     * AlarmAlertFullScreen listens for this broadcast intent, so that other applications
     * can dismiss the alarm (after ACTION_ALARM_ALERT and before ACTION_ALARM_DONE).
     */
    public static final String ACTION_ALARM_DISMISS = "com.uxwine.alarm.ALARM_DISMISS";
    
    /** 
     * This is a private action used by the AlarmService to update the UI to
     * show the alarm has been killed.
     */
    public static final String ACTION_ALARM_KILLED = "com.uxwine.alarm.ALARM_KILLED";
    
    /**
     * This intent is sent from the notification when the user cancels the 
     * snooze alert.
     * */
    public static final String ACTION_SNOOZE_CANCEL = "com.uxwine.alarm.SNOOZE_CANCEL";
    
    /** 
     * This string is used when passing an Alarm object through an intent. 
     * */   
    public static final String EXTRA_ALARM_INTENT = "intent.extra.intent";
    
    /** 
     * Extra in the ACTION_ALARM_KILLED intent to indicate to the user how long the
     * alarm played before being killed.
     * */
    public static final String EXTRA_ALARM_KILLED_TIMEOUT = "intent.extra.timeout";
    
    /** This extra is the raw Alarm object data. It is used in the
     * AlarmManagerService to avoid a ClassNotFoundException when filling in
     *  the Intent extras.
     * */
    public static final String EXTRA_ALARM_RAW = "intent.extra.alarm_raw";
    
    /**
     * This string is used to identify the alarm id passed to SetAlarm from the
     * list of alarms.
     */
    public static final String EXTRA_ALARM_ID = "intent.extra.alarm_id";
    
    
    /**
     * A convenience method to enable or disable an alarm.
     *
     * @param context		 Android Context
     * @param id             corresponds to the _id column
     * @param enabled        corresponds to the ENABLED column
     */

    public static void enableAlarm(final Context context, final int id, boolean enabled) 
    {
        enableAlarmInternal(context, id, enabled);
        setNextAlert(context);
    }
    
    private static void enableAlarmInternal(final Context context, final int id, boolean enabled) 
    {
        enableAlarmInternal(context, 
        		getAlarm(context.getContentResolver(), id),
                enabled);
    }
    
    /**
     * A convenience method to set an alarm in the Alarms
     * content provider.
     * @return Time when the alarm will fire.
     */
    public static long setAlarm(Context context, Alarm alarm) {
    	
    	long timeInMillis = AlarmData.setAlarm(context, alarm);

        if (alarm.enabled) 
        {
            // Disable the snooze if we just changed the snoozed alarm. This
            // only does work if the snoozed alarm is the same as the given
            // alarm.
            // TODO: disableSnoozeAlert should have a better name.
            disableSnoozeAlert(context, alarm.id);

            // Disable the snooze if this alarm fires before the snoozed alarm.
            // This works on every alarm since the user most likely intends to
            // have the modified alarm fire next.
            clearSnoozeIfNeeded(context, timeInMillis);
        }

        setNextAlert(context);

        return timeInMillis;
    }
    
    /**
     * Creates a new Alarm and fills in the given alarm's id.
     */
    public static long addAlarm(Context context, Alarm alarm) 
    {
        long timeInMillis = AlarmData.addAlarm(context, alarm);
        if (alarm.enabled) 
        {
        	//如果闹钟时间早于当前设置的贪睡模式的时间，则清除之。
            clearSnoozeIfNeeded(context, timeInMillis);
        }
        
        //综合考虑所有的闹钟来设置下一个提示的时间。
        setNextAlert(context);
        
        return timeInMillis;
    }
    
    public static void remvoeAlarm(Context context, int alarmId)
    {
    	if (alarmId == -1) return;
    	
    	AlarmData.deleteAlarm(context, alarmId);
    	
        disableSnoozeAlert(context, alarmId);
        setNextAlert(context);
    }
  

    private static void clearSnoozeIfNeeded(Context context, long alarmTime)
    {
        // If this alarm fires before the next snooze, clear the snooze to
        // enable this alarm.
        final Settings.Snooze snooze = Settings.Snooze.getInstance(context); 
        long snoozeTime = snooze.getTime(-1);
        
        if (alarmTime < snoozeTime)
        {
            clearSnoozePreference(context, snooze);
        }
    }
    
    
    /**
     * Called at system startup, on time/timezone change, and whenever
     * the user changes alarm settings.  Activates snooze if set,
     * otherwise loads all alarms, activates next alert.
     */
    public static void setNextAlert(final Context context) 
    {
        if (!enableSnoozeAlert(context)) 
        {
            Alarm alarm = calculateNextAlert(context);
            if (alarm != null) 
            {
                enableAlert(context, alarm, alarm.trigger);
            } 
            else 
            {
                disableAlert(context);
            }
        }
    }
    
    public static Alarm calculateNextAlert(final Context context) 
    {
        Alarm alarm = null;
        long minTime = Long.MAX_VALUE;
        long now = System.currentTimeMillis();
        Cursor cursor = getEnabledAlarm(context.getContentResolver());
        
        if (cursor == null) 
        	return alarm;
        
        if (cursor.moveToFirst())
        {
            do 
            {
                Alarm a = new Alarm(cursor);
                // A time of 0 indicates this is a repeating alarm, so
                // calculate the time to get the next alert.
                if (a.trigger == 0) 
                {
                    a.trigger = calculateAlarm(a);
                } 
                else if (a.trigger < now)
                {
                    Log.d("zhagzhongming", "Disabling expired alarm");
                    // Expired alarm, disable it and move along.
                    enableAlarmInternal(context, a, false);
                    continue;
                }
                if (a.trigger < minTime) {
                    minTime = a.trigger;
                    alarm = a;
                }
            }
            while (cursor.moveToNext());
        }
        cursor.close();
        return alarm;
    }
    
    private static void enableAlarmInternal(final Context context, final Alarm alarm, boolean enabled) 
    {
        if (alarm == null) 
        {
            return;
        }   
        
        if(!enabled)
        {
        	//清除掉与之关联的贪睡模式的闹钟
        	disableSnoozeAlert(context, alarm.id);
        }
        
        //修改数据库
        AlarmData.enableAlarm(context, alarm, enabled);
    }
    
    /**
     * Disable the snooze alert if the given id matches the snooze id.
     */
    public static void disableSnoozeAlert(final Context context, final int id) 
    {
        final Snooze snooze = Snooze.getInstance(context);
        int snoozeId = snooze.getId(-1);
        if (snoozeId == -1) 
        {
            // No snooze set, do nothing.
            return;
        } 
        else if (snoozeId == id) 
        {
            // This is the same id so clear the shared prefs.
            clearSnoozePreference(context, snooze);
        }
    }
    
    /**
     * If there is a snooze set, enable it in AlarmManager
     * @return true if snooze is set
     */
    private static boolean enableSnoozeAlert(final Context context) 
    {  
    	final Snooze snooze = Snooze.getInstance(context);
        int id = snooze.getId(-1);
        if (id == -1) 
        {
            return false;
        }
        long time = snooze.getTime(-1);

        // Get the alarm from the db.
        final Alarm alarm = getAlarm(context.getContentResolver(), id);
        if (alarm == null) 
        {
            return false;
        }
        // The time in the database is either 0 (repeating) or a specific time
        // for a non-repeating alarm. Update this value so the AlarmReceiver
        // has the right time to compare.
        alarm.trigger = time;

        enableAlert(context, alarm, time);
        return true;
    }
    
    public static void saveSnoozeAlert(final Context context, final int id,
            final long time) {
        final Snooze snooze = Snooze.getInstance(context);
        if (id == -1) 
        {
            clearSnoozePreference(context, snooze);
        } 
        else 
        {
        	snooze.set(id, time);
        }
        // Set the next alert after updating the snooze.
        setNextAlert(context);
    }
    
    // Helper to remove the snooze preference. Do not use clear because that
    // will erase the clock preferences. Also clear the snooze notification in
    // the window shade.
    private static void clearSnoozePreference(final Context ctx, final Snooze snooze) 
    {
    	final int id = snooze.getId(-1);
    	if(-1 != id)
    	{
    		//取消状态栏中的显示
    		new AlarmNotify(ctx).cancel(id);
    	}
    	snooze.clear();
    };
    
    /**
     * Sets alert in AlarmManger and StatusBar.  This is what will
     * actually launch the alert when the alarm triggers.
     *
     * @param alarm Alarm.
     * @param atTimeInMillis milliseconds since epoch
     */
    private static void enableAlert(Context context, final Alarm alarm, final long atTimeInMillis) 
    {
        AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(ACTION_ALARM_ALERT);

        // XXX: This is a slight hack to avoid an exception in the remote
        // AlarmManagerService process. The AlarmManager adds extra data to
        // this Intent which causes it to inflate. Since the remote process
        // does not know about the Alarm class, it throws a
        // ClassNotFoundException.
        //
        // To avoid this, we marshall the data ourselves and then parcel a plain
        // byte[] array. The AlarmReceiver class knows to build the Alarm
        // object from the byte[] array.
        Parcel out = Parcel.obtain();
        alarm.writeToParcel(out, 0);
        out.setDataPosition(0);
        intent.putExtra(EXTRA_ALARM_RAW, out.marshall());

        PendingIntent sender = PendingIntent.getBroadcast(
                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
        
        //在状态栏中显示闹钟提醒
        new AlarmNotify(context).updateStated(alarm, 0);
        //在锁屏界面中显示下一个闹钟
        //AlarmTip.tipAlarmSetted(context, atTimeInMillis);
        
        Log.d("zhangzhongming", "enableAlert...");
    }
    
    /**
     * Disables alert in AlarmManger and StatusBar.
     *
     * @param id Alarm ID.
     */
    static void disableAlert(Context context) 
    {
        AlarmManager am = (AlarmManager)
                context.getSystemService(Context.ALARM_SERVICE);
        PendingIntent sender = PendingIntent.getBroadcast(
                context, 0, new Intent(ACTION_ALARM_ALERT),
                PendingIntent.FLAG_CANCEL_CURRENT);
        am.cancel(sender);
        
        // 取消在状态栏中闹钟的显示
        new AlarmNotify(context).updateStated(null, 0);
        // 取消在锁屏界面中下一个闹钟的显示
        //AlarmTip.tipAlarmSetted(context, -1);
    }
}
